home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / C⁄C++ / AIFF DSP v22 / main_src / aiff.c next >
C/C++ Source or Header  |  1995-01-30  |  9KB  |  375 lines

  1. /* general-purpose code for AIFF-based DSP; contains main(). */
  2.  
  3.  
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7.  
  8.  
  9. #include "aiff.h"
  10. #include "plugin_specific.h"
  11. #include "file_selection.h"
  12. #include "type_conversion.h"
  13.  
  14.  
  15. #define BUF_SIZ (1L << 10) /* buffer size, in frames */
  16.  
  17. #define FREAD( dat ) fread( &(dat), sizeof (dat), 1, inf )
  18.  
  19. #define SAMDAT( action, file ) \
  20.    if (!f##action ( d, buflen*nh.framsiz, 1, file )) \
  21.       err( #action"ing sample data" )
  22.  
  23.  
  24. typedef struct
  25. {
  26.    UCHAR id[4],
  27.          si[4];
  28. }
  29. ckhd; /* chunk header: id & size */
  30.  
  31. typedef struct
  32. {
  33.    UCHAR chan[2],  /* number of channels */
  34.          fram[4],  /* number of sample frames */
  35.          wdsi[2],  /* sample word size in bits */
  36.          rate[10]; /* sample rate in frames/sec (80-bit format) */
  37. }
  38. comck;  /* Common Chunk */
  39.  
  40. typedef struct
  41. {
  42.    UCHAR offs[4], /* offset */
  43.          bksi[4]; /* block size */
  44. }
  45. sndck;  /* Sound Chunk beginning */
  46.  
  47. typedef struct
  48. {
  49.    UCHAR frmtyp[4]; /* form type (always 'AIFF') */
  50. }
  51. frmck;  /* Form Chunk beginning */
  52.  
  53. typedef struct
  54. {
  55.    ckhd  frmhd;  /* form chunk header */
  56.    frmck frm;  /* form chunk beginning */
  57.    ckhd  comhd;  /* common chunk header */
  58.    comck com;
  59.    ckhd  sndhd;  /* sound data chunk header */
  60.    sndck snd;
  61. }
  62. basicaiffbeg;  /* basic AIFF beginning: note that this structure allows storage of Common and Sound Data local chunk types only. */
  63.  
  64.  
  65. native_header nh;
  66. void *d; /* audio data buffer */
  67.  
  68.  
  69. static FILE *inf, *ouf; /* input and output files */
  70. static basicaiffbeg ba;
  71.  
  72.  
  73. void err ( char *errmsg )
  74. {
  75.    fprintf( stderr, "FATAL ERROR: %s.\n", errmsg);
  76.    exit(0);
  77. }
  78.  
  79. void warn ( char *warnmsg )
  80. {
  81.    fprintf( stderr, "WARNING: %s.\n", warnmsg);
  82. }
  83.  
  84.  
  85. /* oddpad() rounds up to nearest even number.  This function is needed because if chunk size is odd, there is an uncounted zero pad byte. */
  86. long oddpad( long x )
  87. {
  88.    return x + (x & 1); 
  89. }
  90.  
  91. long min( long a, long b )
  92. {
  93.    return a < b ? a : b;
  94. }
  95.  
  96.  
  97. void open_inf( void )
  98. {
  99. #define INFSTR_LEN 300
  100.    char infstr[INFSTR_LEN];
  101.     
  102.    if ( GETFSTR( infstr, INFSTR_LEN ) ) err( "getting input file name" );
  103.    if ( !(inf = fopen( infstr, "rb" )) ) err( "opening input file" );
  104. }
  105.  
  106. /* processes information in COMM chunk */
  107. void process_com( ckhd hd )
  108. {
  109.    ba.comhd = hd;
  110.    if ( i4(hd.si) != sizeof ba.com ) err( "wrong COMM chunk header size" );
  111.    if ( !FREAD( ba.com ) ) err( "reading COMM chunk body" );
  112.         
  113.    nh.chan = i2(ba.com.chan);
  114.    nh.wdsi = i2(ba.com.wdsi);
  115.    nh.fram = i4(ba.com.fram);
  116.    nh.rate = convert_fr_IEEE_754( ba.com.rate );
  117.    nh.framsiz = ( (nh.wdsi+7) / 8 ) * nh.chan;
  118.  
  119.    printf( "\tsampling rate: %g frames/sec\n"
  120.            "\tword size: %d bits\n"
  121.            "\tchannels: %d\n"
  122.            "\tframe size: %d bytes (derived from word size & channels)\n"
  123.            "\tsample frames: %lu\n",
  124.            nh.rate, nh.wdsi, nh.chan, nh.framsiz, nh.fram );
  125. }
  126.  
  127. /* prints information in SSND chunk, seeks past sample data, returns the byte offset of the sample data in the file */
  128. long process_snd( ckhd hd )
  129. {
  130.    long samdatpos;
  131.  
  132.    ba.sndhd = hd;
  133.  
  134.    if ( !FREAD( ba.snd ) ) err( "reading SSND chunk body" );
  135.  
  136.    if ( i4(ba.snd.bksi) || i4(ba.snd.offs) )
  137.       warn( "blocksize and offset not supported by this program" );
  138.  
  139.    samdatpos = ftell( inf );
  140.  
  141.    if ( fseek( inf, oddpad(i4(hd.si)) - sizeof ba.snd, SEEK_CUR) )
  142.       err( "seeking past sample data");
  143.  
  144.    return samdatpos;
  145. }
  146.  
  147. /* prints information in a text chunk */
  148. void process_txt( ckhd hd )
  149. {
  150. #define TXTBUF_SIZ 80
  151.    char s[TXTBUF_SIZ];
  152.    long bufpos, cksi;
  153.    int buflen;
  154.  
  155.    cksi = oddpad( i4(hd.si) ); /*1*/
  156.    printf( "\ttext: \"" );
  157.    for (bufpos = 0; bufpos < cksi; bufpos += buflen)
  158.    {
  159.       buflen = min( cksi - bufpos, TXTBUF_SIZ );
  160.       if ( !fread( s, buflen, 1, inf ) ) err( "reading text chunk" );
  161.       printf( "%.*s", buflen, s );
  162.    }
  163.    puts ( "\"" );
  164. }
  165. /* 1. OK to include zero pad as part of string in C */
  166.  
  167. void process_ckhd( ckhd hd )
  168. {
  169.    int i;
  170.     
  171.    printf( "\nFound '%.4s' chunk of size %ld\n", hd.id, i4(hd.si) );
  172.    for (i=0; i<4; i++)
  173.       if ( hd.id[i] < ' ' || hd.id[i] > '~' ) warn( "illegal ID character" );
  174.    if ( hd.id[0] == ' ' ) warn( "illegal leading space in ID" );
  175. }
  176.  
  177. #define ID_EQ( id0, id1 ) (!strncmp( (char *) id0, id1, 4 ))
  178.  
  179. /* scan_inf() scans inf, reading in essential header data & finding sample data position (does not actually read sample data). */
  180. void scan_inf ( void )
  181. {
  182.    int comfound = 0, sndfound = 0;
  183.    long frmck_endpos, samdatpos;
  184.    ckhd hd;
  185.  
  186.    if ( !FREAD( ba.frmhd ) || !FREAD( ba.frm ) )
  187.       err( "reading FORM header or type" );
  188.  
  189.    if ( !ID_EQ(ba.frmhd.id, "FORM") || !ID_EQ(ba.frm.frmtyp, "AIFF") )
  190.       err( "Bad FORM chunk id or type" );
  191.  
  192.    process_ckhd( ba.frmhd );
  193.          
  194.    frmck_endpos = i4(ba.frmhd.si) + sizeof hd;
  195.  
  196.    while ( ftell( inf ) < frmck_endpos )
  197.    {
  198.       if ( !FREAD( hd ) ) err( "reading next chunk header" );
  199.         
  200.       process_ckhd( hd );
  201.  
  202.       if ID_EQ( hd.id, "COMM" )
  203.       {
  204.          comfound++;
  205.          process_com( hd );
  206.       } 
  207.       else if ID_EQ( hd.id, "SSND" )
  208.       {
  209.          sndfound++;
  210.          samdatpos = process_snd( hd );
  211.       }
  212.       else if ( ID_EQ( hd.id, "NAME" ) || ID_EQ( hd.id, "AUTH" ) ||
  213.                 ID_EQ( hd.id, "(c) " ) || ID_EQ( hd.id, "ANNO" ))
  214.          process_txt( hd );
  215.       else
  216.       {
  217.          puts( "\tchunk type not used by this program" );
  218.          if ( fseek( inf, oddpad(i4(hd.si)), SEEK_CUR ) )
  219.             err( "seeking past unused chunk body" );
  220.       }
  221.    }
  222.    puts( "\n" );
  223.  
  224.    if ( fgetc( inf ) != EOF ) warn( "File extends beyond FORM chunk" );
  225.    if ( comfound != 1 || sndfound != 1 )
  226.       err( "Not exactly 1 COMM and 1 SSND chunk" );
  227.    if ( nh.fram * nh.framsiz != i4(ba.sndhd.si) - sizeof ba.snd )
  228.       err( "COMM & SSND chunks disagree about sample length" );
  229.     
  230.    if ( fseek( inf, samdatpos, SEEK_SET ) )
  231.       err( "seeking to beginning of sample data" );
  232. }
  233.  
  234. void write_ouf_hd( void )
  235. {
  236.    if ( !fwrite( &ba, sizeof ba, 1, ouf ) ) err( "writing to output file" );
  237. }
  238.  
  239. /* updates (& creates, if necessary) a bar graph of progress */
  240. void prog_report( long bufpos )
  241. {
  242. #define BARMAX 75 /* length of progress report bar */
  243.    char barlen;
  244.    static char oldbarlen = 0, bar[BARMAX];
  245.  
  246.    if (bufpos == 0)
  247.    {
  248.       fputs( "Processing...\n", stderr );
  249.       memset( bar, '*', BARMAX );
  250.       fprintf( stderr, "%.*s\n", BARMAX, bar );
  251.    }
  252.    else
  253.    {
  254.       barlen = (double) (bufpos / nh.fram) * BARMAX;
  255.       fprintf( stderr, "%.*s", barlen - oldbarlen, bar );
  256.       oldbarlen = barlen;
  257.       if ( bufpos == nh.fram ) fputc( '\n', stderr );
  258.    }
  259. }
  260.  
  261. void pad( FILE *ouf )
  262. {
  263.    if ( (nh.fram * nh.framsiz) % 2 )
  264.       if ( fputc( 0, ouf ) == EOF ) err( "padding sample data" );
  265. }
  266.  
  267. void prepare_ba( plugin_info* plugin )
  268. {
  269.    long sample_bytes;
  270.  
  271.    if ( plugin->take_input )
  272.    {
  273.       sample_bytes = i4(ba.sndhd.si) - sizeof ba.snd; /*1*/
  274.       c4( ba.frmhd.si, sizeof ba - sizeof ba.frmhd +
  275.                        oddpad( i4(ba.sndhd.si) - sizeof ba.snd ) );
  276.    }
  277.    else
  278.    {
  279.       sample_bytes = nh.fram * nh.framsiz;
  280.       strncpy( (char *) ba.frmhd.id, "FORM", 4 );
  281.       strncpy( (char *) ba.comhd.id, "COMM", 4 );
  282.       strncpy( (char *) ba.sndhd.id, "SSND", 4 );
  283.  
  284.       c4( ba.frmhd.si, sizeof ba - sizeof ba.frmhd + oddpad(sample_bytes) );
  285.       c4( ba.sndhd.si, sizeof ba.snd + sample_bytes );
  286.       c4( ba.comhd.si, sizeof ba.com );
  287.  
  288.       strncpy( (char *) ba.frm.frmtyp, "AIFF", 4 );
  289.  
  290.       c2(ba.com.chan, nh.chan);
  291.       c2(ba.com.wdsi, nh.wdsi);
  292.       c4(ba.com.fram, nh.fram);
  293.       convert_to_IEEE_754( nh.rate, ba.com.rate );
  294.  
  295.       c4( ba.snd.offs, 0 );
  296.       c4( ba.snd.bksi, 0 );
  297.    }
  298. }
  299. /* 1. Don't trust nh in calculating sample_bytes: a plugin might have changed it illegally. Instead, go back and use ba.sndhd.si. */
  300.  
  301. int main( int argc, char *argv[] )
  302. {
  303.    long bufpos, buflen; /* 1,2 */
  304.    plugin_info *plugin;
  305.  
  306. #ifdef THINK_C
  307.    Think_C_init( &argv );
  308. #endif
  309.  
  310.    plugin = select_plugin();
  311.     
  312.    if ( plugin->take_input )
  313.    {
  314.       open_inf();
  315.       scan_inf();
  316.    }
  317.  
  318.    plugin->init_process();
  319.     
  320.    if ( plugin->make_output )
  321.    {
  322.       prepare_ba( plugin );
  323.       ouf = OPEN_OUF();
  324.       write_ouf_hd();
  325.    }
  326.     
  327.    if (!(d = malloc( nh.framsiz*BUF_SIZ )))
  328.       err( "allocating memory for sample buffer" );
  329.  
  330.    for (bufpos = 0; bufpos < nh.fram; bufpos += buflen )
  331.    {
  332.       prog_report( bufpos );
  333.       buflen = min( nh.fram - bufpos, BUF_SIZ ) ;
  334.  
  335.       if ( plugin->take_input )
  336.       {
  337.          SAMDAT( read,  inf );
  338.          byte_reorder( buflen );      
  339.       }
  340.  
  341.       plugin->process_samdat( buflen );
  342.  
  343.       if ( plugin->make_output )
  344.       {
  345.          byte_reorder( buflen );
  346.          SAMDAT( write, ouf );
  347.       }
  348.    }
  349.  
  350.    prog_report( bufpos );
  351.  
  352.    if ( plugin->make_output )
  353.    {
  354.       pad( ouf );
  355.       fclose( ouf );
  356.    }
  357.    if ( plugin->take_input )
  358.       fclose( inf );
  359.         
  360.    free( d );
  361.  
  362.    plugin->term_process();
  363.    return 0;
  364. }
  365. /*
  366. 1. buffer position: frame # where the next buffer will begin.
  367. 2. buffer length: # of meaningful frames in the buffer.
  368. buflen == BUF_SIZ on all passes except for the last, when buflen == samlen % BUF_SIZ.
  369. The following is an example for bufpos = 4 and buflen = 2.  (In reality these quantities will be much larger.)
  370. | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |   (sample frames)
  371.         |-------| | 
  372.          buflen   |
  373.                   bufpos
  374. */
  375.